#![allow(dead_code)]

use std::{collections::HashMap, hash::Hash};

struct Cacher<F, I, O> {
    exp: F,
    map: HashMap<I, O>,
}

impl<F, I, O> Cacher<F, I, O>
where
    F: Fn(&I) -> O,
    I: Eq + Hash,
{
    pub fn new(exp: F) -> Self {
        Self {
            exp,
            map: HashMap::new(),
        }
    }

    pub fn exec(&mut self, params: I) -> &O {
        self.map
            .entry(params)
            .or_insert_with_key(|params| (self.exp)(params))
    }
}

fn main() {
    let mut cacher = Cacher::new(|v: &i32| {
        println!("call");
        v + 1
    });

    let v1 = *cacher.exec(1);
    let v2 = *cacher.exec(1);
    assert_eq!(v1, v2);

    let v3 = *cacher.exec(2);
    let v4 = *cacher.exec(2);
    assert_eq!(v3, v4);
}
